/***
 * ASM: a very small and fast Java bytecode manipulation framework
 * Copyright (c) 2000-2007 INRIA, France Telecom
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the copyright holders nor the names of its
 *    contributors may be used to endorse or promote products derived from
 *    this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.nutz.repo.org.objectweb.asm;

//import java.io.InputStream;
//import java.io.IOException;

/**
 * A Java class parser to make a {@link ClassVisitor} visit an existing class.
 * This class parses a byte array conforming to the Java class file format and
 * calls the appropriate visit methods of a given class visitor for each field,
 * method and bytecode instruction encountered.
 * 
 * @author Eric Bruneton
 * @author Eugene Kuleshov
 */
public class ClassReader {

    /**
     * True to enable signatures support.
     */
    static final boolean SIGNATURES = true;
    
//    /**
//     * True to enable annotations support.
//     */
//    static final boolean ANNOTATIONS = false;
    
    /**
     * True to enable stack map frames support.
     */
    static final boolean FRAMES = true;
    
//    /**
//     * True to enable bytecode writing support.
//     */
//    static final boolean WRITER = true;
    
    /**
     * True to enable JSR_W and GOTO_W support.
     */
    static final boolean RESIZE = true;
    
//    /**
//     * Flag to skip method code. If this class is set <code>CODE</code>
//     * attribute won't be visited. This can be used, for example, to retrieve
//     * annotations for methods and method parameters.
//     */
//    public static final int SKIP_CODE = 1;

//    /**
//     * Flag to skip the debug information in the class. If this flag is set the
//     * debug information of the class is not visited, i.e. the
//     * {@link MethodVisitor#visitLocalVariable visitLocalVariable} and
//     * {@link MethodVisitor#visitLineNumber visitLineNumber} methods will not be
//     * called.
//     */
//    public static final int SKIP_DEBUG = 2;

//    /**
//     * Flag to skip the stack map frames in the class. If this flag is set the
//     * stack map frames of the class is not visited, i.e. the
//     * {@link MethodVisitor#visitFrame visitFrame} method will not be called.
//     * This flag is useful when the {@link ClassWriter#COMPUTE_FRAMES} option is
//     * used: it avoids visiting frames that will be ignored and recomputed from
//     * scratch in the class writer.
//     */
//    public static final int SKIP_FRAMES = 4;

//    /**
//     * Flag to expand the stack map frames. By default stack map frames are
//     * visited in their original format (i.e. "expanded" for classes whose
//     * version is less than V1_6, and "compressed" for the other classes). If
//     * this flag is set, stack map frames are always visited in expanded format
//     * (this option adds a decompression/recompression step in ClassReader and
//     * ClassWriter which degrades performances quite a lot).
//     */
//    public static final int EXPAND_FRAMES = 8;

//    /**
//     * The class to be parsed. <i>The content of this array must not be
//     * modified. This field is intended for {@link Attribute} sub classes, and
//     * is normally not needed by class generators or adapters.</i>
//     */
//    public final byte[] b;
//
//    /**
//     * The start index of each constant pool item in {@link #b b}, plus one.
//     * The one byte offset skips the constant pool item tag that indicates its
//     * type.
//     */
//    private final int[] items;
//
//    /**
//     * The String objects corresponding to the CONSTANT_Utf8 items. This cache
//     * avoids multiple parsing of a given CONSTANT_Utf8 constant pool item,
//     * which GREATLY improves performances (by a factor 2 to 3). This caching
//     * strategy could be extended to all constant pool items, but its benefit
//     * would not be so great for these items (because they are much less
//     * expensive to parse than CONSTANT_Utf8 items).
//     */
//    private final String[] strings;
//
//    /**
//     * Maximum length of the strings contained in the constant pool of the
//     * class.
//     */
//    private final int maxStringLength;
//
//    /**
//     * Start index of the class header information (access, name...) in
//     * {@link #b b}.
//     */
//    public final int header;
//
//    // ------------------------------------------------------------------------
//    // Constructors
//    // ------------------------------------------------------------------------
//
//    /**
//     * Constructs a new {@link ClassReader} object.
//     * 
//     * @param b the bytecode of the class to be read.
//     */
//    public ClassReader(final byte[] b) {
//        this(b, 0, b.length);
//    }
//
//    /**
//     * Constructs a new {@link ClassReader} object.
//     * 
//     * @param b the bytecode of the class to be read.
//     * @param off the start offset of the class data.
//     * @param len the length of the class data.
//     */
//    public ClassReader(final byte[] b, final int off, final int len) {
//        this.b = b;
//        // parses the constant pool
//        items = new int[readUnsignedShort(off + 8)];
//        int n = items.length;
//        strings = new String[n];
//        int max = 0;
//        int index = off + 10;
//        for (int i = 1; i < n; ++i) {
//            items[i] = index + 1;
//            int size;
//            switch (b[index]) {
//                case ClassWriter.FIELD:
//                case ClassWriter.METH:
//                case ClassWriter.IMETH:
//                case ClassWriter.INT:
//                case ClassWriter.FLOAT:
//                case ClassWriter.NAME_TYPE:
//                    size = 5;
//                    break;
//                case ClassWriter.LONG:
//                case ClassWriter.DOUBLE:
//                    size = 9;
//                    ++i;
//                    break;
//                case ClassWriter.UTF8:
//                    size = 3 + readUnsignedShort(index + 1);
//                    if (size > max) {
//                        max = size;
//                    }
//                    break;
//                // case ClassWriter.CLASS:
//                // case ClassWriter.STR:
//                default:
//                    size = 3;
//                    break;
//            }
//            index += size;
//        }
//        maxStringLength = max;
//        // the class header information starts just after the constant pool
//        header = index;
//    }
//
//    /**
//     * Returns the class's access flags (see {@link Opcodes}). This value may
//     * not reflect Deprecated and Synthetic flags when bytecode is before 1.5
//     * and those flags are represented by attributes.
//     * 
//     * @return the class access flags
//     * 
//     * @see ClassVisitor#visit(int, int, String, String, String, String[])
//     */
//    public int getAccess() {
//        return readUnsignedShort(header);
//    }
//
//    /**
//     * Returns the internal name of the class (see
//     * {@link Type#getInternalName() getInternalName}).
//     * 
//     * @return the internal class name
//     * 
//     * @see ClassVisitor#visit(int, int, String, String, String, String[])
//     */
//    public String getClassName() {
//        return readClass(header + 2, new char[maxStringLength]);
//    }
//
//    /**
//     * Returns the internal of name of the super class (see
//     * {@link Type#getInternalName() getInternalName}). For interfaces, the
//     * super class is {@link Object}.
//     * 
//     * @return the internal name of super class, or <tt>null</tt> for
//     *         {@link Object} class.
//     * 
//     * @see ClassVisitor#visit(int, int, String, String, String, String[])
//     */
//    public String getSuperName() {
//        int n = items[readUnsignedShort(header + 4)];
//        return n == 0 ? null : readUTF8(n, new char[maxStringLength]);
//    }
//
//    /**
//     * Returns the internal names of the class's interfaces (see
//     * {@link Type#getInternalName() getInternalName}).
//     * 
//     * @return the array of internal names for all implemented interfaces or
//     *         <tt>null</tt>.
//     * 
//     * @see ClassVisitor#visit(int, int, String, String, String, String[])
//     */
//    public String[] getInterfaces() {
//        int index = header + 6;
//        int n = readUnsignedShort(index);
//        String[] interfaces = new String[n];
//        if (n > 0) {
//            char[] buf = new char[maxStringLength];
//            for (int i = 0; i < n; ++i) {
//                index += 2;
//                interfaces[i] = readClass(index, buf);
//            }
//        }
//        return interfaces;
//    }
//
//    /**
//     * Copies the constant pool data into the given {@link ClassWriter}. Should
//     * be called before the {@link #accept(ClassVisitor,int)} method.
//     * 
//     * @param classWriter the {@link ClassWriter} to copy constant pool into.
//     */
//    void copyPool(final ClassWriter classWriter) {
//        char[] buf = new char[maxStringLength];
//        int ll = items.length;
//        Item[] items2 = new Item[ll];
//        for (int i = 1; i < ll; i++) {
//            int index = items[i];
//            int tag = b[index - 1];
//            Item item = new Item(i);
//            int nameType;
//            switch (tag) {
//                case ClassWriter.FIELD:
//                case ClassWriter.METH:
//                case ClassWriter.IMETH:
//                    nameType = items[readUnsignedShort(index + 2)];
//                    item.set(tag,
//                            readClass(index, buf),
//                            readUTF8(nameType, buf),
//                            readUTF8(nameType + 2, buf));
//                    break;
//
//                case ClassWriter.INT:
//                    item.set(readInt(index));
//                    break;
//
//                case ClassWriter.FLOAT:
//                    item.set(Float.intBitsToFloat(readInt(index)));
//                    break;
//
//                case ClassWriter.NAME_TYPE:
//                    item.set(tag,
//                            readUTF8(index, buf),
//                            readUTF8(index + 2, buf),
//                            null);
//                    break;
//
//                case ClassWriter.LONG:
//                    item.set(readLong(index));
//                    ++i;
//                    break;
//
//                case ClassWriter.DOUBLE:
//                    item.set(Double.longBitsToDouble(readLong(index)));
//                    ++i;
//                    break;
//
//                case ClassWriter.UTF8: {
//                    String s = strings[i];
//                    if (s == null) {
//                        index = items[i];
//                        s = strings[i] = readUTF(index + 2,
//                                readUnsignedShort(index),
//                                buf);
//                    }
//                    item.set(tag, s, null, null);
//                }
//                    break;
//
//                // case ClassWriter.STR:
//                // case ClassWriter.CLASS:
//                default:
//                    item.set(tag, readUTF8(index, buf), null, null);
//                    break;
//            }
//
//            int index2 = item.hashCode % items2.length;
//            item.next = items2[index2];
//            items2[index2] = item;
//        }
//
//        int off = items[1] - 1;
//        classWriter.pool.putByteArray(b, off, header - off);
//        classWriter.items = items2;
//        classWriter.threshold = (int) (0.75d * ll);
//        classWriter.index = ll;
//    }
//
//    /**
//     * Constructs a new {@link ClassReader} object.
//     * 
//     * @param is an input stream from which to read the class.
//     * @throws IOException if a problem occurs during reading.
//     */
//    public ClassReader(final InputStream is) throws IOException {
//        this(readClass(is));
//    }
//
//    /**
//     * Constructs a new {@link ClassReader} object.
//     * 
//     * @param name the fully qualified name of the class to be read.
//     * @throws IOException if an exception occurs during reading.
//     */
//    public ClassReader(final String name) throws IOException {
//        this(ClassLoader.getSystemResourceAsStream(name.replace('.', '/')
//                + ".class"));
//    }
//
//    /**
//     * Reads the bytecode of a class.
//     * 
//     * @param is an input stream from which to read the class.
//     * @return the bytecode read from the given input stream.
//     * @throws IOException if a problem occurs during reading.
//     */
//    private static byte[] readClass(final InputStream is) throws IOException {
//        if (is == null) {
//            throw new IOException("Class not found");
//        }
//        byte[] b = new byte[is.available()];
//        int len = 0;
//        while (true) {
//            int n = is.read(b, len, b.length - len);
//            if (n == -1) {
//                if (len < b.length) {
//                    byte[] c = new byte[len];
//                    System.arraycopy(b, 0, c, 0, len);
//                    b = c;
//                }
//                return b;
//            }
//            len += n;
//            if (len == b.length) {
//                int last = is.read();
//                if (last < 0) {
//                    return b;
//                }
//                byte[] c = new byte[b.length + 1000];
//                System.arraycopy(b, 0, c, 0, len);
//                c[len++] = (byte) last;
//                b = c;
//            }
//        }
//    }
//
//    // ------------------------------------------------------------------------
//    // Public methods
//    // ------------------------------------------------------------------------
//
//    /**
//     * Makes the given visitor visit the Java class of this {@link ClassReader}.
//     * This class is the one specified in the constructor (see
//     * {@link #ClassReader(byte[]) ClassReader}).
//     * 
//     * @param classVisitor the visitor that must visit this class.
//     * @param flags option flags that can be used to modify the default behavior
//     *        of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES},
//     *        {@link #SKIP_FRAMES}, {@link #SKIP_CODE}.
//     */
//    public void accept(final ClassVisitor classVisitor, final int flags) {
//        accept(classVisitor, new Attribute[0], flags);
//    }
//
//    /**
//     * Makes the given visitor visit the Java class of this {@link ClassReader}.
//     * This class is the one specified in the constructor (see
//     * {@link #ClassReader(byte[]) ClassReader}).
//     * 
//     * @param classVisitor the visitor that must visit this class.
//     * @param attrs prototypes of the attributes that must be parsed during the
//     *        visit of the class. Any attribute whose type is not equal to the
//     *        type of one the prototypes will not be parsed: its byte array
//     *        value will be passed unchanged to the ClassWriter. <i>This may
//     *        corrupt it if this value contains references to the constant pool,
//     *        or has syntactic or semantic links with a class element that has
//     *        been transformed by a class adapter between the reader and the
//     *        writer</i>.
//     * @param flags option flags that can be used to modify the default behavior
//     *        of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES},
//     *        {@link #SKIP_FRAMES}, {@link #SKIP_CODE}.
//     */
//    public void accept(
//        final ClassVisitor classVisitor,
//        final Attribute[] attrs,
//        final int flags)
//    {
//        byte[] b = this.b; // the bytecode array
//        char[] c = new char[maxStringLength]; // buffer used to read strings
//        int i, j, k; // loop variables
//        int u, v, w; // indexes in b
//        Attribute attr;
//
//        int access;
//        String name;
//        String desc;
//        String attrName;
//        String signature;
//        int anns = 0;
//        int ianns = 0;
//        Attribute cattrs = null;
//
//        // visits the header
//        u = header;
//        access = readUnsignedShort(u);
//        name = readClass(u + 2, c);
//        v = items[readUnsignedShort(u + 4)];
//        String superClassName = v == 0 ? null : readUTF8(v, c);
//        String[] implementedItfs = new String[readUnsignedShort(u + 6)];
//        w = 0;
//        u += 8;
//        for (i = 0; i < implementedItfs.length; ++i) {
//            implementedItfs[i] = readClass(u, c);
//            u += 2;
//        }
//
//        boolean skipCode = (flags & SKIP_CODE) != 0;
//        boolean skipDebug = (flags & SKIP_DEBUG) != 0;
//        boolean unzip = (flags & EXPAND_FRAMES) != 0;
//
//        // skips fields and methods
//        v = u;
//        i = readUnsignedShort(v);
//        v += 2;
//        for (; i > 0; --i) {
//            j = readUnsignedShort(v + 6);
//            v += 8;
//            for (; j > 0; --j) {
//                v += 6 + readInt(v + 2);
//            }
//        }
//        i = readUnsignedShort(v);
//        v += 2;
//        for (; i > 0; --i) {
//            j = readUnsignedShort(v + 6);
//            v += 8;
//            for (; j > 0; --j) {
//                v += 6 + readInt(v + 2);
//            }
//        }
//        // reads the class's attributes
//        signature = null;
//        String sourceFile = null;
//        String sourceDebug = null;
//        String enclosingOwner = null;
//        String enclosingName = null;
//        String enclosingDesc = null;
//
//        i = readUnsignedShort(v);
//        v += 2;
//        for (; i > 0; --i) {
//            attrName = readUTF8(v, c);
//            // tests are sorted in decreasing frequency order
//            // (based on frequencies observed on typical classes)
//            if ("SourceFile".equals(attrName)) {
//                sourceFile = readUTF8(v + 6, c);
//            } else if ("InnerClasses".equals(attrName)) {
//                w = v + 6;
//            } else if ("EnclosingMethod".equals(attrName)) {
//                enclosingOwner = readClass(v + 6, c);
//                int item = readUnsignedShort(v + 8);
//                if (item != 0) {
//                    enclosingName = readUTF8(items[item], c);
//                    enclosingDesc = readUTF8(items[item] + 2, c);
//                }
//            } else if (SIGNATURES && "Signature".equals(attrName)) {
//                signature = readUTF8(v + 6, c);
//            } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) {
//                anns = v + 6;
//            } else if ("Deprecated".equals(attrName)) {
//                access |= Opcodes.ACC_DEPRECATED;
//            } else if ("Synthetic".equals(attrName)) {
//                access |= Opcodes.ACC_SYNTHETIC | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE;
//            } else if ("SourceDebugExtension".equals(attrName)) {
//                int len = readInt(v + 2);
//                sourceDebug = readUTF(v + 6, len, new char[len]);
//            } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) {
//                ianns = v + 6;
//            } else {
//                attr = readAttribute(attrs,
//                        attrName,
//                        v + 6,
//                        readInt(v + 2),
//                        c,
//                        -1,
//                        null);
//                if (attr != null) {
//                    attr.next = cattrs;
//                    cattrs = attr;
//                }
//            }
//            v += 6 + readInt(v + 2);
//        }
//        // calls the visit method
//        classVisitor.visit(readInt(4),
//                access,
//                name,
//                signature,
//                superClassName,
//                implementedItfs);
//
//        // calls the visitSource method
//        if (!skipDebug && (sourceFile != null || sourceDebug != null)) {
//            classVisitor.visitSource(sourceFile, sourceDebug);
//        }
//
//        // calls the visitOuterClass method
//        if (enclosingOwner != null) {
//            classVisitor.visitOuterClass(enclosingOwner,
//                    enclosingName,
//                    enclosingDesc);
//        }
//
//        // visits the class annotations
//        if (ANNOTATIONS) {
//            for (i = 1; i >= 0; --i) {
//                v = i == 0 ? ianns : anns;
//                if (v != 0) {
//                    j = readUnsignedShort(v);
//                    v += 2;
//                    for (; j > 0; --j) {
//                        v = readAnnotationValues(v + 2,
//                                c,
//                                true,
//                                classVisitor.visitAnnotation(readUTF8(v, c), i != 0));
//                    }
//                }
//            }
//        }
//
//        // visits the class attributes
//        while (cattrs != null) {
//            attr = cattrs.next;
//            cattrs.next = null;
//            classVisitor.visitAttribute(cattrs);
//            cattrs = attr;
//        }
//
//        // calls the visitInnerClass method
//        if (w != 0) {
//            i = readUnsignedShort(w);
//            w += 2;
//            for (; i > 0; --i) {
//                classVisitor.visitInnerClass(readUnsignedShort(w) == 0
//                        ? null
//                        : readClass(w, c), readUnsignedShort(w + 2) == 0
//                        ? null
//                        : readClass(w + 2, c), readUnsignedShort(w + 4) == 0
//                        ? null
//                        : readUTF8(w + 4, c), readUnsignedShort(w + 6));
//                w += 8;
//            }
//        }
//
//        // visits the fields
//        i = readUnsignedShort(u);
//        u += 2;
//        for (; i > 0; --i) {
//            access = readUnsignedShort(u);
//            name = readUTF8(u + 2, c);
//            desc = readUTF8(u + 4, c);
//            // visits the field's attributes and looks for a ConstantValue
//            // attribute
//            int fieldValueItem = 0;
//            signature = null;
//            anns = 0;
//            ianns = 0;
//            cattrs = null;
//
//            j = readUnsignedShort(u + 6);
//            u += 8;
//            for (; j > 0; --j) {
//                attrName = readUTF8(u, c);
//                // tests are sorted in decreasing frequency order
//                // (based on frequencies observed on typical classes)
//                if ("ConstantValue".equals(attrName)) {
//                    fieldValueItem = readUnsignedShort(u + 6);
//                } else if (SIGNATURES && "Signature".equals(attrName)) {
//                    signature = readUTF8(u + 6, c);
//                } else if ("Deprecated".equals(attrName)) {
//                    access |= Opcodes.ACC_DEPRECATED;
//                } else if ("Synthetic".equals(attrName)) {
//                    access |= Opcodes.ACC_SYNTHETIC  | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE;
//                } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) {
//                    anns = u + 6;
//                } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) {
//                    ianns = u + 6;
//                } else {
//                    attr = readAttribute(attrs,
//                            attrName,
//                            u + 6,
//                            readInt(u + 2),
//                            c,
//                            -1,
//                            null);
//                    if (attr != null) {
//                        attr.next = cattrs;
//                        cattrs = attr;
//                    }
//                }
//                u += 6 + readInt(u + 2);
//            }
//            // visits the field
//            FieldVisitor fv = classVisitor.visitField(access,
//                    name,
//                    desc,
//                    signature,
//                    fieldValueItem == 0 ? null : readConst(fieldValueItem, c));
//            // visits the field annotations and attributes
//            if (fv != null) {
//                if (ANNOTATIONS) {
//                    for (j = 1; j >= 0; --j) {
//                        v = j == 0 ? ianns : anns;
//                        if (v != 0) {
//                            k = readUnsignedShort(v);
//                            v += 2;
//                            for (; k > 0; --k) {
//                                v = readAnnotationValues(v + 2,
//                                        c,
//                                        true,
//                                        fv.visitAnnotation(readUTF8(v, c), j != 0));
//                            }
//                        }
//                    }
//                }
//                while (cattrs != null) {
//                    attr = cattrs.next;
//                    cattrs.next = null;
//                    fv.visitAttribute(cattrs);
//                    cattrs = attr;
//                }
//                fv.visitEnd();
//            }
//        }
//
//        // visits the methods
//        i = readUnsignedShort(u);
//        u += 2;
//        for (; i > 0; --i) {
//            int u0 = u + 6;
//            access = readUnsignedShort(u);
//            name = readUTF8(u + 2, c);
//            desc = readUTF8(u + 4, c);
//            signature = null;
//            anns = 0;
//            ianns = 0;
//            int dann = 0;
//            int mpanns = 0;
//            int impanns = 0;
//            cattrs = null;
//            v = 0;
//            w = 0;
//
//            // looks for Code and Exceptions attributes
//            j = readUnsignedShort(u + 6);
//            u += 8;
//            for (; j > 0; --j) {
//                attrName = readUTF8(u, c);
//                int attrSize = readInt(u + 2);
//                u += 6;
//                // tests are sorted in decreasing frequency order
//                // (based on frequencies observed on typical classes)
//                if ("Code".equals(attrName)) {
//                    if (!skipCode) {
//                        v = u;
//                    }
//                } else if ("Exceptions".equals(attrName)) {
//                    w = u;
//                } else if (SIGNATURES && "Signature".equals(attrName)) {
//                    signature = readUTF8(u, c);
//                } else if ("Deprecated".equals(attrName)) {
//                    access |= Opcodes.ACC_DEPRECATED;
//                } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) {
//                    anns = u;
//                } else if (ANNOTATIONS && "AnnotationDefault".equals(attrName)) {
//                    dann = u;
//                } else if ("Synthetic".equals(attrName)) {
//                    access |= Opcodes.ACC_SYNTHETIC | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE;
//                } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) {
//                    ianns = u;
//                } else if (ANNOTATIONS && "RuntimeVisibleParameterAnnotations".equals(attrName))
//                {
//                    mpanns = u;
//                } else if (ANNOTATIONS && "RuntimeInvisibleParameterAnnotations".equals(attrName))
//                {
//                    impanns = u;
//                } else {
//                    attr = readAttribute(attrs,
//                            attrName,
//                            u,
//                            attrSize,
//                            c,
//                            -1,
//                            null);
//                    if (attr != null) {
//                        attr.next = cattrs;
//                        cattrs = attr;
//                    }
//                }
//                u += attrSize;
//            }
//            // reads declared exceptions
//            String[] exceptions;
//            if (w == 0) {
//                exceptions = null;
//            } else {
//                exceptions = new String[readUnsignedShort(w)];
//                w += 2;
//                for (j = 0; j < exceptions.length; ++j) {
//                    exceptions[j] = readClass(w, c);
//                    w += 2;
//                }
//            }
//
//            // visits the method's code, if any
//            MethodVisitor mv = classVisitor.visitMethod(access,
//                    name,
//                    desc,
//                    signature,
//                    exceptions);
//
//            if (mv != null) {
//                /*
//                 * if the returned MethodVisitor is in fact a MethodWriter, it
//                 * means there is no method adapter between the reader and the
//                 * writer. If, in addition, the writer's constant pool was
//                 * copied from this reader (mw.cw.cr == this), and the signature
//                 * and exceptions of the method have not been changed, then it
//                 * is possible to skip all visit events and just copy the
//                 * original code of the method to the writer (the access, name
//                 * and descriptor can have been changed, this is not important
//                 * since they are not copied as is from the reader).
//                 */
//                if (WRITER && mv instanceof MethodWriter) {
//                    MethodWriter mw = (MethodWriter) mv;
//                    if (mw.cw.cr == this) {
//                        if (signature == mw.signature) {
//                            boolean sameExceptions = false;
//                            if (exceptions == null) {
//                                sameExceptions = mw.exceptionCount == 0;
//                            } else {
//                                if (exceptions.length == mw.exceptionCount) {
//                                    sameExceptions = true;
//                                    for (j = exceptions.length - 1; j >= 0; --j)
//                                    {
//                                        w -= 2;
//                                        if (mw.exceptions[j] != readUnsignedShort(w))
//                                        {
//                                            sameExceptions = false;
//                                            break;
//                                        }
//                                    }
//                                }
//                            }
//                            if (sameExceptions) {
//                                /*
//                                 * we do not copy directly the code into
//                                 * MethodWriter to save a byte array copy
//                                 * operation. The real copy will be done in
//                                 * ClassWriter.toByteArray().
//                                 */
//                                mw.classReaderOffset = u0;
//                                mw.classReaderLength = u - u0;
//                                continue;
//                            }
//                        }
//                    }
//                }
//
//                if (ANNOTATIONS && dann != 0) {
//                    AnnotationVisitor dv = mv.visitAnnotationDefault();
//                    readAnnotationValue(dann, c, null, dv);
//                    if (dv != null) {
//                        dv.visitEnd();
//                    }
//                }
//                if (ANNOTATIONS) {
//                    for (j = 1; j >= 0; --j) {
//                        w = j == 0 ? ianns : anns;
//                        if (w != 0) {
//                            k = readUnsignedShort(w);
//                            w += 2;
//                            for (; k > 0; --k) {
//                                w = readAnnotationValues(w + 2,
//                                        c,
//                                        true,
//                                        mv.visitAnnotation(readUTF8(w, c), j != 0));
//                            }
//                        }
//                    }
//                }
//                if (ANNOTATIONS && mpanns != 0) {
//                    readParameterAnnotations(mpanns, desc, c, true, mv);
//                }
//                if (ANNOTATIONS && impanns != 0) {
//                    readParameterAnnotations(impanns, desc, c, false, mv);
//                }
//                while (cattrs != null) {
//                    attr = cattrs.next;
//                    cattrs.next = null;
//                    mv.visitAttribute(cattrs);
//                    cattrs = attr;
//                }
//            }
//
//            if (mv != null && v != 0) {
//                int maxStack = readUnsignedShort(v);
//                int maxLocals = readUnsignedShort(v + 2);
//                int codeLength = readInt(v + 4);
//                v += 8;
//
//                int codeStart = v;
//                int codeEnd = v + codeLength;
//
//                mv.visitCode();
//
//                // 1st phase: finds the labels
//                int label;
//                Label[] labels = new Label[codeLength + 2];
//                readLabel(codeLength + 1, labels);
//                while (v < codeEnd) {
//                    w = v - codeStart;
//                    int opcode = b[v] & 0xFF;
//                    switch (ClassWriter.TYPE[opcode]) {
//                        case ClassWriter.NOARG_INSN:
//                        case ClassWriter.IMPLVAR_INSN:
//                            v += 1;
//                            break;
//                        case ClassWriter.LABEL_INSN:
//                            readLabel(w + readShort(v + 1), labels);
//                            v += 3;
//                            break;
//                        case ClassWriter.LABELW_INSN:
//                            readLabel(w + readInt(v + 1), labels);
//                            v += 5;
//                            break;
//                        case ClassWriter.WIDE_INSN:
//                            opcode = b[v + 1] & 0xFF;
//                            if (opcode == Opcodes.IINC) {
//                                v += 6;
//                            } else {
//                                v += 4;
//                            }
//                            break;
//                        case ClassWriter.TABL_INSN:
//                            // skips 0 to 3 padding bytes*
//                            v = v + 4 - (w & 3);
//                            // reads instruction
//                            readLabel(w + readInt(v), labels);
//                            j = readInt(v + 8) - readInt(v + 4) + 1;
//                            v += 12;
//                            for (; j > 0; --j) {
//                                readLabel(w + readInt(v), labels);
//                                v += 4;
//                            }
//                            break;
//                        case ClassWriter.LOOK_INSN:
//                            // skips 0 to 3 padding bytes*
//                            v = v + 4 - (w & 3);
//                            // reads instruction
//                            readLabel(w + readInt(v), labels);
//                            j = readInt(v + 4);
//                            v += 8;
//                            for (; j > 0; --j) {
//                                readLabel(w + readInt(v + 4), labels);
//                                v += 8;
//                            }
//                            break;
//                        case ClassWriter.VAR_INSN:
//                        case ClassWriter.SBYTE_INSN:
//                        case ClassWriter.LDC_INSN:
//                            v += 2;
//                            break;
//                        case ClassWriter.SHORT_INSN:
//                        case ClassWriter.LDCW_INSN:
//                        case ClassWriter.FIELDORMETH_INSN:
//                        case ClassWriter.TYPE_INSN:
//                        case ClassWriter.IINC_INSN:
//                            v += 3;
//                            break;
//                        case ClassWriter.ITFDYNMETH_INSN:
//                            v += 5;
//                            break;
//                        // case MANA_INSN:
//                        default:
//                            v += 4;
//                            break;
//                    }
//                }
//                // parses the try catch entries
//                j = readUnsignedShort(v);
//                v += 2;
//                for (; j > 0; --j) {
//                    Label start = readLabel(readUnsignedShort(v), labels);
//                    Label end = readLabel(readUnsignedShort(v + 2), labels);
//                    Label handler = readLabel(readUnsignedShort(v + 4), labels);
//                    int type = readUnsignedShort(v + 6);
//                    if (type == 0) {
//                        mv.visitTryCatchBlock(start, end, handler, null);
//                    } else {
//                        mv.visitTryCatchBlock(start,
//                                end,
//                                handler,
//                                readUTF8(items[type], c));
//                    }
//                    v += 8;
//                }
//                // parses the local variable, line number tables, and code
//                // attributes
//                int varTable = 0;
//                int varTypeTable = 0;
//                int stackMap = 0;
//                int stackMapSize = 0;
//                int frameCount = 0;
//                int frameMode = 0;
//                int frameOffset = 0;
//                int frameLocalCount = 0;
//                int frameLocalDiff = 0;
//                int frameStackCount = 0;
//                Object[] frameLocal = null;
//                Object[] frameStack = null;
//                boolean zip = true;
//                cattrs = null;
//                j = readUnsignedShort(v);
//                v += 2;
//                for (; j > 0; --j) {
//                    attrName = readUTF8(v, c);
//                    if ("LocalVariableTable".equals(attrName)) {
//                        if (!skipDebug) {
//                            varTable = v + 6;
//                            k = readUnsignedShort(v + 6);
//                            w = v + 8;
//                            for (; k > 0; --k) {
//                                label = readUnsignedShort(w);
//                                if (labels[label] == null) {
//                                    readLabel(label, labels).status |= Label.DEBUG;
//                                }
//                                label += readUnsignedShort(w + 2);
//                                if (labels[label] == null) {
//                                    readLabel(label, labels).status |= Label.DEBUG;
//                                }
//                                w += 10;
//                            }
//                        }
//                    } else if ("LocalVariableTypeTable".equals(attrName)) {
//                        varTypeTable = v + 6;
//                    } else if ("LineNumberTable".equals(attrName)) {
//                        if (!skipDebug) {
//                            k = readUnsignedShort(v + 6);
//                            w = v + 8;
//                            for (; k > 0; --k) {
//                                label = readUnsignedShort(w);
//                                if (labels[label] == null) {
//                                    readLabel(label, labels).status |= Label.DEBUG;
//                                }
//                                labels[label].line = readUnsignedShort(w + 2);
//                                w += 4;
//                            }
//                        }
//                    } else if (FRAMES && "StackMapTable".equals(attrName)) {
//                        if ((flags & SKIP_FRAMES) == 0) {
//                            stackMap = v + 8;
//                            stackMapSize = readInt(v + 2);
//                            frameCount = readUnsignedShort(v + 6);
//                        }
//                        /*
//                         * here we do not extract the labels corresponding to
//                         * the attribute content. This would require a full
//                         * parsing of the attribute, which would need to be
//                         * repeated in the second phase (see below). Instead the
//                         * content of the attribute is read one frame at a time
//                         * (i.e. after a frame has been visited, the next frame
//                         * is read), and the labels it contains are also
//                         * extracted one frame at a time. Thanks to the ordering
//                         * of frames, having only a "one frame lookahead" is not
//                         * a problem, i.e. it is not possible to see an offset
//                         * smaller than the offset of the current insn and for
//                         * which no Label exist.
//                         */
//                        /*
//                         * This is not true for UNINITIALIZED type offsets. We
//                         * solve this by parsing the stack map table without a
//                         * full decoding (see below).
//                         */
//                    } else if (FRAMES && "StackMap".equals(attrName)) {
//                        if ((flags & SKIP_FRAMES) == 0) {
//                            stackMap = v + 8;
//                            stackMapSize = readInt(v + 2);
//                            frameCount = readUnsignedShort(v + 6);
//                            zip = false;
//                        }
//                        /*
//                         * IMPORTANT! here we assume that the frames are
//                         * ordered, as in the StackMapTable attribute, although
//                         * this is not guaranteed by the attribute format.
//                         */
//                    } else {
//                        for (k = 0; k < attrs.length; ++k) {
//                            if (attrs[k].type.equals(attrName)) {
//                                attr = attrs[k].read(this,
//                                        v + 6,
//                                        readInt(v + 2),
//                                        c,
//                                        codeStart - 8,
//                                        labels);
//                                if (attr != null) {
//                                    attr.next = cattrs;
//                                    cattrs = attr;
//                                }
//                            }
//                        }
//                    }
//                    v += 6 + readInt(v + 2);
//                }
//
//                // 2nd phase: visits each instruction
//                if (FRAMES && stackMap != 0) {
//                    // creates the very first (implicit) frame from the method
//                    // descriptor
//                    frameLocal = new Object[maxLocals];
//                    frameStack = new Object[maxStack];
//                    if (unzip) {
//                        int local = 0;
//                        if ((access & Opcodes.ACC_STATIC) == 0) {
//                            if ("<init>".equals(name)) {
//                                frameLocal[local++] = Opcodes.UNINITIALIZED_THIS;
//                            } else {
//                                frameLocal[local++] = readClass(header + 2, c);
//                            }
//                        }
//                        j = 1;
//                        loop: while (true) {
//                            k = j;
//                            switch (desc.charAt(j++)) {
//                                case 'Z':
//                                case 'C':
//                                case 'B':
//                                case 'S':
//                                case 'I':
//                                    frameLocal[local++] = Opcodes.INTEGER;
//                                    break;
//                                case 'F':
//                                    frameLocal[local++] = Opcodes.FLOAT;
//                                    break;
//                                case 'J':
//                                    frameLocal[local++] = Opcodes.LONG;
//                                    break;
//                                case 'D':
//                                    frameLocal[local++] = Opcodes.DOUBLE;
//                                    break;
//                                case '[':
//                                    while (desc.charAt(j) == '[') {
//                                        ++j;
//                                    }
//                                    if (desc.charAt(j) == 'L') {
//                                        ++j;
//                                        while (desc.charAt(j) != ';') {
//                                            ++j;
//                                        }
//                                    }
//                                    frameLocal[local++] = desc.substring(k, ++j);
//                                    break;
//                                case 'L':
//                                    while (desc.charAt(j) != ';') {
//                                        ++j;
//                                    }
//                                    frameLocal[local++] = desc.substring(k + 1,
//                                            j++);
//                                    break;
//                                default:
//                                    break loop;
//                            }
//                        }
//                        frameLocalCount = local;
//                    }
//                    /*
//                     * for the first explicit frame the offset is not
//                     * offset_delta + 1 but only offset_delta; setting the
//                     * implicit frame offset to -1 allow the use of the
//                     * "offset_delta + 1" rule in all cases
//                     */
//                    frameOffset = -1;
//                    /*
//                     * Finds labels for UNINITIALIZED frame types. Instead of
//                     * decoding each element of the stack map table, we look
//                     * for 3 consecutive bytes that "look like" an UNINITIALIZED
//                     * type (tag 8, offset within code bounds, NEW instruction
//                     * at this offset). We may find false positives (i.e. not 
//                     * real UNINITIALIZED types), but this should be rare, and 
//                     * the only consequence will be the creation of an unneeded 
//                     * label. This is better than creating a label for each NEW
//                     * instruction, and faster than fully decoding the whole 
//                     * stack map table.
//                     */
//                    for (j = stackMap; j < stackMap + stackMapSize - 2; ++j) {
//                        if (b[j] == 8) { // UNINITIALIZED FRAME TYPE
//                            k = readUnsignedShort(j + 1);
//                            if (k >= 0 && k < codeLength) { // potential offset
//                                if ((b[codeStart + k] & 0xFF) == Opcodes.NEW) { // NEW at this offset
//                                    readLabel(k, labels);
//                                }
//                            }
//                        }
//                    }
//                }
//                v = codeStart;
//                Label l;
//                while (v < codeEnd) {
//                    w = v - codeStart;
//
//                    l = labels[w];
//                    if (l != null) {
//                        mv.visitLabel(l);
//                        if (!skipDebug && l.line > 0) {
//                            mv.visitLineNumber(l.line, l);
//                        }
//                    }
//
//                    while (FRAMES && frameLocal != null
//                            && (frameOffset == w || frameOffset == -1))
//                    {
//                        // if there is a frame for this offset,
//                        // makes the visitor visit it,
//                        // and reads the next frame if there is one.
//                        if (!zip || unzip) {
//                            mv.visitFrame(Opcodes.F_NEW,
//                                    frameLocalCount,
//                                    frameLocal,
//                                    frameStackCount,
//                                    frameStack);
//                        } else if (frameOffset != -1) {
//                            mv.visitFrame(frameMode,
//                                    frameLocalDiff,
//                                    frameLocal,
//                                    frameStackCount,
//                                    frameStack);
//                        }
//
//                        if (frameCount > 0) {
//                            int tag, delta, n;
//                            if (zip) {
//                                tag = b[stackMap++] & 0xFF;
//                            } else {
//                                tag = MethodWriter.FULL_FRAME;
//                                frameOffset = -1;
//                            }
//                            frameLocalDiff = 0;
//                            if (tag < MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME)
//                            {
//                                delta = tag;
//                                frameMode = Opcodes.F_SAME;
//                                frameStackCount = 0;
//                            } else if (tag < MethodWriter.RESERVED) {
//                                delta = tag
//                                        - MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME;
//                                stackMap = readFrameType(frameStack,
//                                        0,
//                                        stackMap,
//                                        c,
//                                        labels);
//                                frameMode = Opcodes.F_SAME1;
//                                frameStackCount = 1;
//                            } else {
//                                delta = readUnsignedShort(stackMap);
//                                stackMap += 2;
//                                if (tag == MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)
//                                {
//                                    stackMap = readFrameType(frameStack,
//                                            0,
//                                            stackMap,
//                                            c,
//                                            labels);
//                                    frameMode = Opcodes.F_SAME1;
//                                    frameStackCount = 1;
//                                } else if (tag >= MethodWriter.CHOP_FRAME
//                                        && tag < MethodWriter.SAME_FRAME_EXTENDED)
//                                {
//                                    frameMode = Opcodes.F_CHOP;
//                                    frameLocalDiff = MethodWriter.SAME_FRAME_EXTENDED
//                                            - tag;
//                                    frameLocalCount -= frameLocalDiff;
//                                    frameStackCount = 0;
//                                } else if (tag == MethodWriter.SAME_FRAME_EXTENDED)
//                                {
//                                    frameMode = Opcodes.F_SAME;
//                                    frameStackCount = 0;
//                                } else if (tag < MethodWriter.FULL_FRAME) {
//                                    j = unzip ? frameLocalCount : 0;
//                                    for (k = tag
//                                            - MethodWriter.SAME_FRAME_EXTENDED; k > 0; k--)
//                                    {
//                                        stackMap = readFrameType(frameLocal,
//                                                j++,
//                                                stackMap,
//                                                c,
//                                                labels);
//                                    }
//                                    frameMode = Opcodes.F_APPEND;
//                                    frameLocalDiff = tag
//                                            - MethodWriter.SAME_FRAME_EXTENDED;
//                                    frameLocalCount += frameLocalDiff;
//                                    frameStackCount = 0;
//                                } else { // if (tag == FULL_FRAME) {
//                                    frameMode = Opcodes.F_FULL;
//                                    n = frameLocalDiff = frameLocalCount = readUnsignedShort(stackMap);
//                                    stackMap += 2;
//                                    for (j = 0; n > 0; n--) {
//                                        stackMap = readFrameType(frameLocal,
//                                                j++,
//                                                stackMap,
//                                                c,
//                                                labels);
//                                    }
//                                    n = frameStackCount = readUnsignedShort(stackMap);
//                                    stackMap += 2;
//                                    for (j = 0; n > 0; n--) {
//                                        stackMap = readFrameType(frameStack,
//                                                j++,
//                                                stackMap,
//                                                c,
//                                                labels);
//                                    }
//                                }
//                            }
//                            frameOffset += delta + 1;
//                            readLabel(frameOffset, labels);
//
//                            --frameCount;
//                        } else {
//                            frameLocal = null;
//                        }
//                    }
//
//                    int opcode = b[v] & 0xFF;
//                    switch (ClassWriter.TYPE[opcode]) {
//                        case ClassWriter.NOARG_INSN:
//                            mv.visitInsn(opcode);
//                            v += 1;
//                            break;
//                        case ClassWriter.IMPLVAR_INSN:
//                            if (opcode > Opcodes.ISTORE) {
//                                opcode -= 59; // ISTORE_0
//                                mv.visitVarInsn(Opcodes.ISTORE + (opcode >> 2),
//                                        opcode & 0x3);
//                            } else {
//                                opcode -= 26; // ILOAD_0
//                                mv.visitVarInsn(Opcodes.ILOAD + (opcode >> 2),
//                                        opcode & 0x3);
//                            }
//                            v += 1;
//                            break;
//                        case ClassWriter.LABEL_INSN:
//                            mv.visitJumpInsn(opcode, labels[w
//                                    + readShort(v + 1)]);
//                            v += 3;
//                            break;
//                        case ClassWriter.LABELW_INSN:
//                            mv.visitJumpInsn(opcode - 33, labels[w
//                                    + readInt(v + 1)]);
//                            v += 5;
//                            break;
//                        case ClassWriter.WIDE_INSN:
//                            opcode = b[v + 1] & 0xFF;
//                            if (opcode == Opcodes.IINC) {
//                                mv.visitIincInsn(readUnsignedShort(v + 2),
//                                        readShort(v + 4));
//                                v += 6;
//                            } else {
//                                mv.visitVarInsn(opcode,
//                                        readUnsignedShort(v + 2));
//                                v += 4;
//                            }
//                            break;
//                        case ClassWriter.TABL_INSN:
//                            // skips 0 to 3 padding bytes
//                            v = v + 4 - (w & 3);
//                            // reads instruction
//                            label = w + readInt(v);
//                            int min = readInt(v + 4);
//                            int max = readInt(v + 8);
//                            v += 12;
//                            Label[] table = new Label[max - min + 1];
//                            for (j = 0; j < table.length; ++j) {
//                                table[j] = labels[w + readInt(v)];
//                                v += 4;
//                            }
//                            mv.visitTableSwitchInsn(min,
//                                    max,
//                                    labels[label],
//                                    table);
//                            break;
//                        case ClassWriter.LOOK_INSN:
//                            // skips 0 to 3 padding bytes
//                            v = v + 4 - (w & 3);
//                            // reads instruction
//                            label = w + readInt(v);
//                            j = readInt(v + 4);
//                            v += 8;
//                            int[] keys = new int[j];
//                            Label[] values = new Label[j];
//                            for (j = 0; j < keys.length; ++j) {
//                                keys[j] = readInt(v);
//                                values[j] = labels[w + readInt(v + 4)];
//                                v += 8;
//                            }
//                            mv.visitLookupSwitchInsn(labels[label],
//                                    keys,
//                                    values);
//                            break;
//                        case ClassWriter.VAR_INSN:
//                            mv.visitVarInsn(opcode, b[v + 1] & 0xFF);
//                            v += 2;
//                            break;
//                        case ClassWriter.SBYTE_INSN:
//                            mv.visitIntInsn(opcode, b[v + 1]);
//                            v += 2;
//                            break;
//                        case ClassWriter.SHORT_INSN:
//                            mv.visitIntInsn(opcode, readShort(v + 1));
//                            v += 3;
//                            break;
//                        case ClassWriter.LDC_INSN:
//                            mv.visitLdcInsn(readConst(b[v + 1] & 0xFF, c));
//                            v += 2;
//                            break;
//                        case ClassWriter.LDCW_INSN:
//                            mv.visitLdcInsn(readConst(readUnsignedShort(v + 1),
//                                    c));
//                            v += 3;
//                            break;
//                        case ClassWriter.FIELDORMETH_INSN:
//                        case ClassWriter.ITFDYNMETH_INSN:
//                            int cpIndex = items[readUnsignedShort(v + 1)];
//                            String iowner;
//                            // INVOKEDYNAMIC is receiverless
//                            if (opcode == Opcodes.INVOKEDYNAMIC) {
//                                iowner = Opcodes.INVOKEDYNAMIC_OWNER;
//                            } else {
//                                iowner = readClass(cpIndex, c);
//                                cpIndex = items[readUnsignedShort(cpIndex + 2)];
//                            }
//                            String iname = readUTF8(cpIndex, c);
//                            String idesc = readUTF8(cpIndex + 2, c);
//                            if (opcode < Opcodes.INVOKEVIRTUAL) {
//                                mv.visitFieldInsn(opcode, iowner, iname, idesc);
//                            } else {
//                                mv.visitMethodInsn(opcode, iowner, iname, idesc);
//                            }
//                            if (opcode == Opcodes.INVOKEINTERFACE || opcode == Opcodes.INVOKEDYNAMIC) {
//                                v += 5;
//                            } else {
//                                v += 3;
//                            }
//                            break;
//                        case ClassWriter.TYPE_INSN:
//                            mv.visitTypeInsn(opcode, readClass(v + 1, c));
//                            v += 3;
//                            break;
//                        case ClassWriter.IINC_INSN:
//                            mv.visitIincInsn(b[v + 1] & 0xFF, b[v + 2]);
//                            v += 3;
//                            break;
//                        // case MANA_INSN:
//                        default:
//                            mv.visitMultiANewArrayInsn(readClass(v + 1, c),
//                                    b[v + 3] & 0xFF);
//                            v += 4;
//                            break;
//                    }
//                }
//                l = labels[codeEnd - codeStart];
//                if (l != null) {
//                    mv.visitLabel(l);
//                }
//                // visits the local variable tables
//                if (!skipDebug && varTable != 0) {
//                    int[] typeTable = null;
//                    if (varTypeTable != 0) {
//                        k = readUnsignedShort(varTypeTable) * 3;
//                        w = varTypeTable + 2;
//                        typeTable = new int[k];
//                        while (k > 0) {
//                            typeTable[--k] = w + 6; // signature
//                            typeTable[--k] = readUnsignedShort(w + 8); // index
//                            typeTable[--k] = readUnsignedShort(w); // start
//                            w += 10;
//                        }
//                    }
//                    k = readUnsignedShort(varTable);
//                    w = varTable + 2;
//                    for (; k > 0; --k) {
//                        int start = readUnsignedShort(w);
//                        int length = readUnsignedShort(w + 2);
//                        int index = readUnsignedShort(w + 8);
//                        String vsignature = null;
//                        if (typeTable != null) {
//                            for (int a = 0; a < typeTable.length; a += 3) {
//                                if (typeTable[a] == start
//                                        && typeTable[a + 1] == index)
//                                {
//                                    vsignature = readUTF8(typeTable[a + 2], c);
//                                    break;
//                                }
//                            }
//                        }
//                        mv.visitLocalVariable(readUTF8(w + 4, c),
//                                readUTF8(w + 6, c),
//                                vsignature,
//                                labels[start],
//                                labels[start + length],
//                                index);
//                        w += 10;
//                    }
//                }
//                // visits the other attributes
//                while (cattrs != null) {
//                    attr = cattrs.next;
//                    cattrs.next = null;
//                    mv.visitAttribute(cattrs);
//                    cattrs = attr;
//                }
//                // visits the max stack and max locals values
//                mv.visitMaxs(maxStack, maxLocals);
//            }
//
//            if (mv != null) {
//                mv.visitEnd();
//            }
//        }
//
//        // visits the end of the class
//        classVisitor.visitEnd();
//    }
//
//    /**
//     * Reads parameter annotations and makes the given visitor visit them.
//     * 
//     * @param v start offset in {@link #b b} of the annotations to be read.
//     * @param desc the method descriptor.
//     * @param buf buffer to be used to call {@link #readUTF8 readUTF8},
//     *        {@link #readClass(int,char[]) readClass} or
//     *        {@link #readConst readConst}.
//     * @param visible <tt>true</tt> if the annotations to be read are visible
//     *        at runtime.
//     * @param mv the visitor that must visit the annotations.
//     */
//    private void readParameterAnnotations(
//        int v,
//        final String desc,
//        final char[] buf,
//        final boolean visible,
//        final MethodVisitor mv)
//    {
//        int i;
//        int n = b[v++] & 0xFF;
//        // workaround for a bug in javac (javac compiler generates a parameter
//        // annotation array whose size is equal to the number of parameters in
//        // the Java source file, while it should generate an array whose size is
//        // equal to the number of parameters in the method descriptor - which
//        // includes the synthetic parameters added by the compiler). This work-
//        // around supposes that the synthetic parameters are the first ones.
//        int synthetics = Type.getArgumentTypes(desc).length - n;
//        AnnotationVisitor av;
//        for (i = 0; i < synthetics; ++i) {
//            // virtual annotation to detect synthetic parameters in MethodWriter 
//            av = mv.visitParameterAnnotation(i, "Ljava/lang/Synthetic;", false);
//            if (av != null) {
//                av.visitEnd();
//            }
//        }
//        for (; i < n + synthetics; ++i) {
//            int j = readUnsignedShort(v);
//            v += 2;
//            for (; j > 0; --j) {
//                av = mv.visitParameterAnnotation(i, readUTF8(v, buf), visible);
//                v = readAnnotationValues(v + 2, buf, true, av);
//            }
//        }
//    }
//
//    /**
//     * Reads the values of an annotation and makes the given visitor visit them.
//     * 
//     * @param v the start offset in {@link #b b} of the values to be read
//     *        (including the unsigned short that gives the number of values).
//     * @param buf buffer to be used to call {@link #readUTF8 readUTF8},
//     *        {@link #readClass(int,char[]) readClass} or
//     *        {@link #readConst readConst}.
//     * @param named if the annotation values are named or not.
//     * @param av the visitor that must visit the values.
//     * @return the end offset of the annotation values.
//     */
//    private int readAnnotationValues(
//        int v,
//        final char[] buf,
//        final boolean named,
//        final AnnotationVisitor av)
//    {
//        int i = readUnsignedShort(v);
//        v += 2;
//        if (named) {
//            for (; i > 0; --i) {
//                v = readAnnotationValue(v + 2, buf, readUTF8(v, buf), av);
//            }
//        } else {
//            for (; i > 0; --i) {
//                v = readAnnotationValue(v, buf, null, av);
//            }
//        }
//        if (av != null) {
//            av.visitEnd();
//        }
//        return v;
//    }
//
//    /**
//     * Reads a value of an annotation and makes the given visitor visit it.
//     * 
//     * @param v the start offset in {@link #b b} of the value to be read (<i>not
//     *        including the value name constant pool index</i>).
//     * @param buf buffer to be used to call {@link #readUTF8 readUTF8},
//     *        {@link #readClass(int,char[]) readClass} or
//     *        {@link #readConst readConst}.
//     * @param name the name of the value to be read.
//     * @param av the visitor that must visit the value.
//     * @return the end offset of the annotation value.
//     */
//    private int readAnnotationValue(
//        int v,
//        final char[] buf,
//        final String name,
//        final AnnotationVisitor av)
//    {
//        int i;
//        if (av == null) {
//            switch (b[v] & 0xFF) {
//                case 'e': // enum_const_value
//                    return v + 5;
//                case '@': // annotation_value
//                    return readAnnotationValues(v + 3, buf, true, null);
//                case '[': // array_value
//                    return readAnnotationValues(v + 1, buf, false, null);
//                default:
//                    return v + 3;
//            }
//        }
//        switch (b[v++] & 0xFF) {
//            case 'I': // pointer to CONSTANT_Integer
//            case 'J': // pointer to CONSTANT_Long
//            case 'F': // pointer to CONSTANT_Float
//            case 'D': // pointer to CONSTANT_Double
//                av.visit(name, readConst(readUnsignedShort(v), buf));
//                v += 2;
//                break;
//            case 'B': // pointer to CONSTANT_Byte
//                av.visit(name,
//                        new Byte((byte) readInt(items[readUnsignedShort(v)])));
//                v += 2;
//                break;
//            case 'Z': // pointer to CONSTANT_Boolean
//                av.visit(name, readInt(items[readUnsignedShort(v)]) == 0
//                        ? Boolean.FALSE
//                        : Boolean.TRUE);
//                v += 2;
//                break;
//            case 'S': // pointer to CONSTANT_Short
//                av.visit(name,
//                        new Short((short) readInt(items[readUnsignedShort(v)])));
//                v += 2;
//                break;
//            case 'C': // pointer to CONSTANT_Char
//                av.visit(name,
//                        new Character((char) readInt(items[readUnsignedShort(v)])));
//                v += 2;
//                break;
//            case 's': // pointer to CONSTANT_Utf8
//                av.visit(name, readUTF8(v, buf));
//                v += 2;
//                break;
//            case 'e': // enum_const_value
//                av.visitEnum(name, readUTF8(v, buf), readUTF8(v + 2, buf));
//                v += 4;
//                break;
//            case 'c': // class_info
//                av.visit(name, Type.getType(readUTF8(v, buf)));
//                v += 2;
//                break;
//            case '@': // annotation_value
//                v = readAnnotationValues(v + 2,
//                        buf,
//                        true,
//                        av.visitAnnotation(name, readUTF8(v, buf)));
//                break;
//            case '[': // array_value
//                int size = readUnsignedShort(v);
//                v += 2;
//                if (size == 0) {
//                    return readAnnotationValues(v - 2,
//                            buf,
//                            false,
//                            av.visitArray(name));
//                }
//                switch (this.b[v++] & 0xFF) {
//                    case 'B':
//                        byte[] bv = new byte[size];
//                        for (i = 0; i < size; i++) {
//                            bv[i] = (byte) readInt(items[readUnsignedShort(v)]);
//                            v += 3;
//                        }
//                        av.visit(name, bv);
//                        --v;
//                        break;
//                    case 'Z':
//                        boolean[] zv = new boolean[size];
//                        for (i = 0; i < size; i++) {
//                            zv[i] = readInt(items[readUnsignedShort(v)]) != 0;
//                            v += 3;
//                        }
//                        av.visit(name, zv);
//                        --v;
//                        break;
//                    case 'S':
//                        short[] sv = new short[size];
//                        for (i = 0; i < size; i++) {
//                            sv[i] = (short) readInt(items[readUnsignedShort(v)]);
//                            v += 3;
//                        }
//                        av.visit(name, sv);
//                        --v;
//                        break;
//                    case 'C':
//                        char[] cv = new char[size];
//                        for (i = 0; i < size; i++) {
//                            cv[i] = (char) readInt(items[readUnsignedShort(v)]);
//                            v += 3;
//                        }
//                        av.visit(name, cv);
//                        --v;
//                        break;
//                    case 'I':
//                        int[] iv = new int[size];
//                        for (i = 0; i < size; i++) {
//                            iv[i] = readInt(items[readUnsignedShort(v)]);
//                            v += 3;
//                        }
//                        av.visit(name, iv);
//                        --v;
//                        break;
//                    case 'J':
//                        long[] lv = new long[size];
//                        for (i = 0; i < size; i++) {
//                            lv[i] = readLong(items[readUnsignedShort(v)]);
//                            v += 3;
//                        }
//                        av.visit(name, lv);
//                        --v;
//                        break;
//                    case 'F':
//                        float[] fv = new float[size];
//                        for (i = 0; i < size; i++) {
//                            fv[i] = Float.intBitsToFloat(readInt(items[readUnsignedShort(v)]));
//                            v += 3;
//                        }
//                        av.visit(name, fv);
//                        --v;
//                        break;
//                    case 'D':
//                        double[] dv = new double[size];
//                        for (i = 0; i < size; i++) {
//                            dv[i] = Double.longBitsToDouble(readLong(items[readUnsignedShort(v)]));
//                            v += 3;
//                        }
//                        av.visit(name, dv);
//                        --v;
//                        break;
//                    default:
//                        v = readAnnotationValues(v - 3,
//                                buf,
//                                false,
//                                av.visitArray(name));
//                }
//        }
//        return v;
//    }
//
//    private int readFrameType(
//        final Object[] frame,
//        final int index,
//        int v,
//        final char[] buf,
//        final Label[] labels)
//    {
//        int type = b[v++] & 0xFF;
//        switch (type) {
//            case 0:
//                frame[index] = Opcodes.TOP;
//                break;
//            case 1:
//                frame[index] = Opcodes.INTEGER;
//                break;
//            case 2:
//                frame[index] = Opcodes.FLOAT;
//                break;
//            case 3:
//                frame[index] = Opcodes.DOUBLE;
//                break;
//            case 4:
//                frame[index] = Opcodes.LONG;
//                break;
//            case 5:
//                frame[index] = Opcodes.NULL;
//                break;
//            case 6:
//                frame[index] = Opcodes.UNINITIALIZED_THIS;
//                break;
//            case 7: // Object
//                frame[index] = readClass(v, buf);
//                v += 2;
//                break;
//            default: // Uninitialized
//                frame[index] = readLabel(readUnsignedShort(v), labels);
//                v += 2;
//        }
//        return v;
//    }
//
//    /**
//     * Returns the label corresponding to the given offset. The default
//     * implementation of this method creates a label for the given offset if it
//     * has not been already created.
//     * 
//     * @param offset a bytecode offset in a method.
//     * @param labels the already created labels, indexed by their offset. If a
//     *        label already exists for offset this method must not create a new
//     *        one. Otherwise it must store the new label in this array.
//     * @return a non null Label, which must be equal to labels[offset].
//     */
//    protected Label readLabel(int offset, Label[] labels) {
//        if (labels[offset] == null) {
//            labels[offset] = new Label();
//        }
//        return labels[offset];
//    }
//
//    /**
//     * Reads an attribute in {@link #b b}.
//     * 
//     * @param attrs prototypes of the attributes that must be parsed during the
//     *        visit of the class. Any attribute whose type is not equal to the
//     *        type of one the prototypes is ignored (i.e. an empty
//     *        {@link Attribute} instance is returned).
//     * @param type the type of the attribute.
//     * @param off index of the first byte of the attribute's content in
//     *        {@link #b b}. The 6 attribute header bytes, containing the type
//     *        and the length of the attribute, are not taken into account here
//     *        (they have already been read).
//     * @param len the length of the attribute's content.
//     * @param buf buffer to be used to call {@link #readUTF8 readUTF8},
//     *        {@link #readClass(int,char[]) readClass} or
//     *        {@link #readConst readConst}.
//     * @param codeOff index of the first byte of code's attribute content in
//     *        {@link #b b}, or -1 if the attribute to be read is not a code
//     *        attribute. The 6 attribute header bytes, containing the type and
//     *        the length of the attribute, are not taken into account here.
//     * @param labels the labels of the method's code, or <tt>null</tt> if the
//     *        attribute to be read is not a code attribute.
//     * @return the attribute that has been read, or <tt>null</tt> to skip this
//     *         attribute.
//     */
//    private Attribute readAttribute(
//        final Attribute[] attrs,
//        final String type,
//        final int off,
//        final int len,
//        final char[] buf,
//        final int codeOff,
//        final Label[] labels)
//    {
//        for (int i = 0; i < attrs.length; ++i) {
//            if (attrs[i].type.equals(type)) {
//                return attrs[i].read(this, off, len, buf, codeOff, labels);
//            }
//        }
//        return new Attribute(type).read(this, off, len, null, -1, null);
//    }
//
//    // ------------------------------------------------------------------------
//    // Utility methods: low level parsing
//    // ------------------------------------------------------------------------
//
//    /**
//     * Returns the start index of the constant pool item in {@link #b b}, plus
//     * one. <i>This method is intended for {@link Attribute} sub classes, and is
//     * normally not needed by class generators or adapters.</i>
//     * 
//     * @param item the index a constant pool item.
//     * @return the start index of the constant pool item in {@link #b b}, plus
//     *         one.
//     */
//    public int getItem(final int item) {
//        return items[item];
//    }
//
//    /**
//     * Reads a byte value in {@link #b b}. <i>This method is intended for
//     * {@link Attribute} sub classes, and is normally not needed by class
//     * generators or adapters.</i>
//     * 
//     * @param index the start index of the value to be read in {@link #b b}.
//     * @return the read value.
//     */
//    public int readByte(final int index) {
//        return b[index] & 0xFF;
//    }
//
//    /**
//     * Reads an unsigned short value in {@link #b b}. <i>This method is
//     * intended for {@link Attribute} sub classes, and is normally not needed by
//     * class generators or adapters.</i>
//     * 
//     * @param index the start index of the value to be read in {@link #b b}.
//     * @return the read value.
//     */
//    public int readUnsignedShort(final int index) {
//        byte[] b = this.b;
//        return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);
//    }
//
//    /**
//     * Reads a signed short value in {@link #b b}. <i>This method is intended
//     * for {@link Attribute} sub classes, and is normally not needed by class
//     * generators or adapters.</i>
//     * 
//     * @param index the start index of the value to be read in {@link #b b}.
//     * @return the read value.
//     */
//    public short readShort(final int index) {
//        byte[] b = this.b;
//        return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF));
//    }
//
//    /**
//     * Reads a signed int value in {@link #b b}. <i>This method is intended for
//     * {@link Attribute} sub classes, and is normally not needed by class
//     * generators or adapters.</i>
//     * 
//     * @param index the start index of the value to be read in {@link #b b}.
//     * @return the read value.
//     */
//    public int readInt(final int index) {
//        byte[] b = this.b;
//        return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16)
//                | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF);
//    }
//
//    /**
//     * Reads a signed long value in {@link #b b}. <i>This method is intended
//     * for {@link Attribute} sub classes, and is normally not needed by class
//     * generators or adapters.</i>
//     * 
//     * @param index the start index of the value to be read in {@link #b b}.
//     * @return the read value.
//     */
//    public long readLong(final int index) {
//        long l1 = readInt(index);
//        long l0 = readInt(index + 4) & 0xFFFFFFFFL;
//        return (l1 << 32) | l0;
//    }
//
//    /**
//     * Reads an UTF8 string constant pool item in {@link #b b}. <i>This method
//     * is intended for {@link Attribute} sub classes, and is normally not needed
//     * by class generators or adapters.</i>
//     * 
//     * @param index the start index of an unsigned short value in {@link #b b},
//     *        whose value is the index of an UTF8 constant pool item.
//     * @param buf buffer to be used to read the item. This buffer must be
//     *        sufficiently large. It is not automatically resized.
//     * @return the String corresponding to the specified UTF8 item.
//     */
//    public String readUTF8(int index, final char[] buf) {
//        int item = readUnsignedShort(index);
//        String s = strings[item];
//        if (s != null) {
//            return s;
//        }
//        index = items[item];
//        return strings[item] = readUTF(index + 2, readUnsignedShort(index), buf);
//    }
//
//    /**
//     * Reads UTF8 string in {@link #b b}.
//     * 
//     * @param index start offset of the UTF8 string to be read.
//     * @param utfLen length of the UTF8 string to be read.
//     * @param buf buffer to be used to read the string. This buffer must be
//     *        sufficiently large. It is not automatically resized.
//     * @return the String corresponding to the specified UTF8 string.
//     */
//    private String readUTF(int index, final int utfLen, final char[] buf) {
//        int endIndex = index + utfLen;
//        byte[] b = this.b;
//        int strLen = 0;
//        int c;
//        int st = 0;
//        char cc = 0;
//        while (index < endIndex) {
//            c = b[index++];
//            switch (st) {
//                case 0:
//                    c = c & 0xFF;
//                    if (c < 0x80) {  // 0xxxxxxx
//                        buf[strLen++] = (char) c;
//                    } else if (c < 0xE0 && c > 0xBF) {  // 110x xxxx 10xx xxxx
//                        cc = (char) (c & 0x1F);
//                        st = 1;
//                    } else {  // 1110 xxxx 10xx xxxx 10xx xxxx
//                        cc = (char) (c & 0x0F);
//                        st = 2;
//                    }
//                    break;
//                    
//                case 1:  // byte 2 of 2-byte char or byte 3 of 3-byte char 
//                    buf[strLen++] = (char) ((cc << 6) | (c & 0x3F));
//                    st = 0;
//                    break;
//                    
//                case 2:  // byte 2 of 3-byte char
//                    cc = (char) ((cc << 6) | (c & 0x3F));
//                    st = 1;
//                    break;
//            }
//        }
//        return new String(buf, 0, strLen);
//    }
//    
//    /**
//     * Reads a class constant pool item in {@link #b b}. <i>This method is
//     * intended for {@link Attribute} sub classes, and is normally not needed by
//     * class generators or adapters.</i>
//     * 
//     * @param index the start index of an unsigned short value in {@link #b b},
//     *        whose value is the index of a class constant pool item.
//     * @param buf buffer to be used to read the item. This buffer must be
//     *        sufficiently large. It is not automatically resized.
//     * @return the String corresponding to the specified class item.
//     */
//    public String readClass(final int index, final char[] buf) {
//        // computes the start index of the CONSTANT_Class item in b
//        // and reads the CONSTANT_Utf8 item designated by
//        // the first two bytes of this CONSTANT_Class item
//        return readUTF8(items[readUnsignedShort(index)], buf);
//    }
//
//    /**
//     * Reads a numeric or string constant pool item in {@link #b b}. <i>This
//     * method is intended for {@link Attribute} sub classes, and is normally not
//     * needed by class generators or adapters.</i>
//     * 
//     * @param item the index of a constant pool item.
//     * @param buf buffer to be used to read the item. This buffer must be
//     *        sufficiently large. It is not automatically resized.
//     * @return the {@link Integer}, {@link Float}, {@link Long},
//     *         {@link Double}, {@link String} or {@link Type} corresponding to
//     *         the given constant pool item.
//     */
//    public Object readConst(final int item, final char[] buf) {
//        int index = items[item];
//        switch (b[index - 1]) {
//            case ClassWriter.INT:
//                return new Integer(readInt(index));
//            case ClassWriter.FLOAT:
//                return new Float(Float.intBitsToFloat(readInt(index)));
//            case ClassWriter.LONG:
//                return new Long(readLong(index));
//            case ClassWriter.DOUBLE:
//                return new Double(Double.longBitsToDouble(readLong(index)));
//            case ClassWriter.CLASS:
//                return Type.getObjectType(readUTF8(index, buf));
//                // case ClassWriter.STR:
//            default:
//                return readUTF8(index, buf);
//        }
//    }
}
